﻿using System;
using System.Linq;
using System.Text;
using ISRecord.Model;
using ISRecord.View.Services;
using ISRecord.View.Properties;
using System.Windows.Input;
using System.Reflection;
using System.ComponentModel;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ISRecord.View.Views;
using ISRecord.Model.CustomeException;
using System.Windows;
using GalaSoft.MvvmLight.Messaging;
namespace ISRecord.View.ViewsModel
{
    public sealed class OutboxViewModel : WorkspaceViewModel
    {
        #region "Fields"

        OutboxRepository repository;
        DelegateCommand saveCommand;
        DelegateCommand deleteCommand;
        DelegateCommand findCommand;
        DelegateCommand clearCommand;

        Action<string> infoMessage = (msg) => Helper.ShowMessage(msg, MessagePrompetType.ReportInfo);
        Action<string> errorMessage = (msg) => Helper.ShowMessage(msg, MessagePrompetType.ReportError);
        Func<string, bool> confirmationMessage = (msg) => { return Helper.ShowMessage(msg, MessagePrompetType.AskQuestion); };
        
        #endregion

        public OutboxViewModel()
        {
            Sender2006Entities context = new Sender2006Entities();
            repository = new OutboxRepository(context);
            ModelController(ModelState.NotLoaded, outbox);
            base.DisplayName = Resources.OutboxRecordDispalyName;
        }


        #region "General Properties"

        #endregion

        #region "General Methods"

        #endregion

        #region "Binding Properties"
        [Bindable(true)]
        public string OutboxNo
        {
            get
            {
                if (OutboxModel == null) return ""; //Defend aginst Model Null
                return OutboxModel.OutboxNo;
            }
            //set
            //{
            //    if (OutboxModel == null) return;  //Defend aginst Model Null
            //    OutboxModel.OutboxNo = value;
            //    RaisePropertyChanged("OutboxNo");
            //}


        }
        [Bindable(true)]
        public string OutboxDate
        {
            get
            {
                if (OutboxModel == null) return ""; //Defend aginst Model Null
                return OutboxModel.OutboxDate;
            }
            set
            {
                if (OutboxModel == null) return; //Defend aginst Model Null
                OutboxModel.OutboxDate = value;
                RaisePropertyChanged("OutboxDate");
            }

        }
        [Bindable(true)]
        public string Subject
        {
            get
            {
                if (OutboxModel == null) return ""; //Defend aginst Model Null
                return OutboxModel.Subject;
            }
            set
            {
                if (OutboxModel == null) return; //Defend aginst Model Null
                OutboxModel.Subject = value;
                RaisePropertyChanged("Subject");
            }
        }
        [Bindable(true)]
        public string GoingTo
        {
            get
            {
                if (OutboxModel == null) return ""; //Defend aginst Model Null
                return OutboxModel.GoingTo;
            }
            set
            {
                if (OutboxModel == null) return; //Defend aginst Model Null
                OutboxModel.GoingTo = value;
                RaisePropertyChanged("GoingTo");
            }
        }
        [Bindable(true)]
        public string AttachmentNo
        {
            get
            {
                if (OutboxModel == null) return ""; //Defend aginst Model Null
                return OutboxModel.AttachmentNo;
            }
            set
            {
                if (OutboxModel == null) return; //Defend aginst Model Null
                OutboxModel.AttachmentNo = value;
                RaisePropertyChanged("AttachmentNo");
            }
        }
        [Bindable(true)]
        public string Notes
        {
            get
            {
                if (OutboxModel == null) return ""; //Defend aginst Model Null
                return OutboxModel.Notes;
            }
            set
            {
                if (OutboxModel == null) return; //Defend aginst Model Null
                OutboxModel.Notes = value;
                RaisePropertyChanged("Notes");
            }
        }
        [Bindable(true)]
        public bool OutboxNoEnabled
        {
            get
            {
                return OutboxModel == null;
            }
        }

        
        #endregion
        #region "Commands Properties"
        public ICommand SaveCommand
        {
            get
            {
                if (saveCommand == null)
                {
                    saveCommand = new DelegateCommand(Save, CanSave, "Save");
                }
                return saveCommand;
            }
        }

        public bool CanSave(object obj)
        {
            return OutboxModel != null;
        }

        public ICommand DeleteCommand
        {
            get
            {
                if (deleteCommand == null)
                {
                    deleteCommand = new DelegateCommand(Delete, CanDelete, "Delete");
                }
                return deleteCommand;
            }
        }
        private void Delete(object obj)
        {
            if (Helper.ConfirmDelete())
            {
                try
                {
                    repository.Delete(OutboxModel);
                    repository.Save();
                    ModelController(ModelState.Deleted);
                }
                catch (DataModelException ex)
                {
                    Logger.Error(ex.Message, "OutboxViewModel", "Delete");
                    errorMessage(ex.Message);
                }
            }
            
        }
        private bool CanDelete(object obj)
        {
            return OutboxModel != null;
        }
        public ICommand FindCommand
        {
            get
            {
                if (findCommand == null)
                {
                    findCommand = new DelegateCommand(Find, CanFind, "Find");
                }
                return findCommand;
            }
        }
        private void Find(object obj)
        {
            try
            {
                ObservableCollection<Outbox> outboxList = new ObservableCollection<Outbox>(repository.GetAll());
                SearchOutboxViewModel vm = new SearchOutboxViewModel(outboxList);
                SearchOutboxView view = new SearchOutboxView(vm)
                {
                    WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen
                };

                if (view.ShowDialog() == true)
                {
                    Outbox selected = vm.SelectedOutbox;
                    if (selected != null)
                    {
                        ModelController(ModelState.Founded, selected);
                    }
                }
            }
            catch (Exception ex)
            {
                if (ex.InnerException != null)
                {
                    Logger.Error(ex.InnerException.Message, "AllOutboxesViewMode", "Find");
                }
                else
                {
                    Logger.Error(ex.Message, "AllOutboxesViewModel", "Find");
                }
                errorMessage(ex.Message);
            }
        }
        private bool CanFind(object obj)
        {
            return OutboxModel == null;
        }
        public ICommand ClearCommand
        {
            get
            {
                if (clearCommand == null)
                {
                    clearCommand = new DelegateCommand(ClearModel);
                }
                return clearCommand;
            }
        }
        private void ClearModel(object obj)
        {
            //Only if we have Outbox object and that object has been 
            if (OutboxModel != null)
            {
                if (ModelHasChanges)
                {
                   string msg = Resources.PrompetToSaveMsg;
                   if (confirmationMessage(msg) == false)
                   {
                       return;
                   }
                }
                ModelController(ModelState.Cleared);
                
            }
            
        }
        #endregion

        #region "Base implementation"

        void Save(object obj)
        {
            if (!OutboxModel.IsValid)
            {
                var errors = outbox.GetRuleViolations();
                var temp = new StringBuilder();
                foreach (var ruleViolation in errors)
                {
                    temp.AppendFormat("{0}{1}", ruleViolation, "\n");

                }
                infoMessage(temp.ToString());
                return;
            }
            try
            {
                repository.Save();
            }
            catch (DataModelException e)
            {
                Logger.Error(e, "OutboxViewModel", "Save");
                errorMessage(e.Message);
            }
           
        }

        public override void Cleanup()
        {
            repository.Dispose();
            base.Cleanup();
        }

        public override bool ModelHasChanges
        {
            get
            {
                return repository.HasChanges;
            }
        }
        #endregion

        #region "Helper Methods Will be called from View"
        internal void CreateOutbox()
        {
            try
            {
                Outbox o = new Outbox()
                {
                    OutboxNo = OutboxRepository.GenerateOutboxNo(Settings.Default.ActiveYear).ToString(),
                };
                ModelController(ModelState.Constructed, o);
            }
            catch (Exception ex)
            {
                if (ex.InnerException != null)
                {
                    Logger.Error(ex.InnerException.Message, "OutboxeViewMode", "CreateOutbox");
                }
                else
                {
                    Logger.Error(ex.Message, "OutboxeViewModel", "CreateOutbox");
                }
                errorMessage(ex.Message);
            }
        }


        internal void FindOutbox(string no)
        {
            Outbox o;
            string currentYear = Settings.Default.ActiveYear;
            if (repository.TryFindOutbox(no, currentYear, out o))
            {
                ModelController(ModelState.Founded, o);
            }
            else
            {
                string msg = Resources.CannotFindOutbox;
                infoMessage(msg);
            }
        }
        #endregion

        #region "Model Instance Controller"
        
        //Instance of model.
        Outbox outbox;
        //Access the instance via property, so it will notify changes in state.
        Outbox OutboxModel
        {
            get { return outbox; }
            set
            {
                if (outbox != value)
                {
                    outbox = value;
                    RefershBinding();
                }
            }
        }

        private void RefershBinding()
        {
            //Query expression 
            var bindableProperties = from p in typeof(OutboxViewModel).GetProperties()
                                     where p.GetCustomAttributes(true).Cast<Attribute>().Contains(new BindableAttribute(true))
                                     select p;
            //Method calls with Lambda
            //var m = typeof(OutboxViewModel).GetProperties()
            //    .Where(p => p.GetCustomAttributes(true).Cast<Attribute>().Contains(new BindableAttribute(true)))
            //    .Select(p => p);

            foreach (var p in bindableProperties)
            {
                RaisePropertyChanged(p.Name);
            }
           
            
           
        }


        private void ModelController(ModelState state, Outbox obj = null)
        {
            switch (state)
            {
                case ModelState.NotLoaded:
                    OutboxModel = obj;
                    RefershBinding();
                    break;
                case ModelState.Constructed:
                    OutboxModel = obj;
                    repository.Add(OutboxModel);
                    DisplayName = string.Format("{0}{1}", Resources.Nolbl, OutboxNo);
                    RefershBinding();
                    break;
                case ModelState.Founded:
                    OutboxModel = obj;
                    DisplayName = string.Format("{0}{1}", Resources.Nolbl, OutboxNo);
                    break;
                case ModelState.Deleted:
                    OutboxModel = obj;
                    //Uncomment if you want to support Undo delete operation.
                    //The scenario supported now is save changes immediately after Delete command executed.
                    //repository.RefreshContext();
                    break;
                case ModelState.Cleared:
                    if (OutboxModel.EntityState == System.Data.EntityState.Added)
                    {
                        repository.Delete(OutboxModel);
                    }
                    else
                    {
                        repository.ChangeEntityState(OutboxModel, System.Data.EntityState.Unchanged);
                    }
                    OutboxModel =obj;
                    DisplayName = string.Format("{0}{1}", Resources.Nolbl, OutboxNo);
                    
                    break;
            }
            Messenger.Default.Send<ModelState>(state);
        }
        #endregion
        public override string DisplayName
        {
            get
            {
                return base.DisplayName;
            }
            protected set
            {
                base.DisplayName = value;
                RaisePropertyChanged("DisplayName");
            }
        }

    }
}
